package cn.sylinx.horm.dialect.fs;

import cn.sylinx.horm.core.common.OrderBy;
import cn.sylinx.horm.core.common.TypedParameter;
import cn.sylinx.horm.exception.HORMException;
import cn.sylinx.horm.type.handler.TypeHandler;
import cn.sylinx.horm.util.GLog;
import cn.sylinx.horm.util.StrKit;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * fluent sql support, KEY WORD CAN NOT BE USED!
 *
 * @param
 * @author han
 */
public class PlainFS {

    public static final String AND_STR = " AND ";
    public static final String BLANK_STR = "";

    protected StringBuilder conditionSQL = new StringBuilder();
    protected StringBuilder orderBySQL = new StringBuilder();
    protected StringBuilder groupBySQL = new StringBuilder();
    protected StringBuilder limitSQL = new StringBuilder();
    protected List<Object> paramValues = new ArrayList<>(8);
    protected Map<String, Object> updateColumns = new LinkedHashMap<>(8);
    protected Map<String, String> updateColumnExpression = new LinkedHashMap<>(8);
    protected String selectColumns;
    protected String selectExpressionColumns;
    protected boolean distinct = false;
    protected boolean count = false;
    protected String[] primaryKeyColumn;
    protected String tableName;
    protected Map<String, TypeHandler<?>> typeHandlerMap = null;
    protected String preEscape;
    protected String postEscape;
    protected String hint;

    protected String tablePostfix = BLANK_STR;
    protected String tablePrefix = BLANK_STR;

    public static PlainFS of(String tableName) {
        return of(BLANK_STR, tableName, BLANK_STR);
    }

    public static PlainFS of(String tablePrefix, String tableName, String tablePostfix) {
        return new PlainFS().init(tablePrefix, tableName, tablePostfix);
    }

    public PlainFS init(String tablePrefix, String tableName, String tablePostfix) {
        return parse(tablePrefix, tableName, tablePostfix);
    }

    protected String[] getEscape() {
        return new String[]{BLANK_STR, BLANK_STR};
    }

    protected String getDefaultTableName() {
        return preEscape + tablePrefix + tableName + tablePostfix + postEscape;
    }

    private PlainFS parse(String tablePrefix, String tableName, String tablePostfix) {

        this.tablePrefix = tablePrefix == null ? BLANK_STR : tablePrefix.trim();
        this.tablePostfix = tablePostfix == null ? BLANK_STR : tablePostfix.trim();

        String[] escapes = getEscape();
        this.preEscape = escapes[0];
        this.postEscape = escapes[1];

        this.tableName = tableName;

        this.primaryKeyColumn = new String[]{"id"};

        if (this.typeHandlerMap == null) {
            this.typeHandlerMap = new HashMap<>();
        }

        return this;
    }

    public PlainFS setEscape(String preEscape, String postEscape) {
        this.preEscape = preEscape;
        this.postEscape = postEscape;
        return this;
    }

    public PlainFS select(String columns) {
        if (null != this.selectColumns) {
            throw new HORMException("Select method can only be called once.");
        }
        this.selectColumns = mapColumns(columns);
        return this;
    }

    /**
     * 使用native sql表达式， 不要用 ',' 号结束
     *
     * @param exp
     * @return
     */
    public PlainFS selectExp(String exp) {
        if (null != this.selectExpressionColumns) {
            throw new HORMException("SelectExp method can only be called once.");
        }
        this.selectExpressionColumns = exp;
        return this;
    }

    public PlainFS select(String... columnArray) {
        if (null != this.selectColumns) {
            throw new HORMException("Select method can only be called once.");
        }
        this.selectColumns = mapColumns(columnArray);
        return this;
    }

    /**
     * 放在sql最前面的提示
     *
     * @param hint
     * @return
     */
    public PlainFS hint(String hint) {
        this.hint = hint;
        return this;
    }

    public PlainFS distinct() {
        this.distinct = true;
        return this;
    }

    public PlainFS count() {
        this.count = true;
        return this;
    }

    private String mapColumns(String[] columnArray) {

        this.beforeCheck();
        if (columnArray == null || columnArray.length == 0) {
            return null;
        }
        StringBuilder mappedClumns = new StringBuilder();
        for (String column : columnArray) {
            mappedClumns.append(mapColumn(column.trim())).append(",");
        }
        mappedClumns.deleteCharAt(mappedClumns.length() - 1);
        return mappedClumns.toString();
    }

    private String mapColumns(String columns) {

        this.beforeCheck();
        if (StrKit.isBlank(columns)) {
            return null;
        }
        StringBuilder mappedClumns = new StringBuilder();
        String[] columnArray = columns.split(",");
        for (String column : columnArray) {
            mappedClumns.append(mapColumn(column.trim())).append(",");
        }
        mappedClumns.deleteCharAt(mappedClumns.length() - 1);
        return mappedClumns.toString();
    }

    protected String mapColumn(String column) {
        return preEscape + column + postEscape;
    }

    /**
     * where condition
     *
     * @param column
     * @return SqlGenerator
     */
    public PlainFS where(String column) {
        String trueStatement = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueStatement);
        return this;
    }

    /**
     * just where condition
     *
     * @return
     */
    public PlainFS where() {
        conditionSQL.append(AND_STR);
        return this;
    }

    /**
     * 表达式
     *
     * @param exp
     * @return
     */
    public PlainFS whereExp(String exp) {
        conditionSQL.append(AND_STR).append(exp);
        return this;
    }

    /**
     * where condition, simultaneous setting value
     *
     * @param column
     * @param value  column name
     * @return SqlGenerator
     */
    public PlainFS where(String column, Object value) {
        String trueStatement = mapColumn(column) + " = ?";
        conditionSQL.append(AND_STR).append(trueStatement);
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * Equals statement
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS eq(Object value) {
        conditionSQL.append(" = ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "IS NOT NULL" statement
     *
     * @return SqlGenerator
     */
    public PlainFS notNull() {
        conditionSQL.append(" IS NOT NULL");
        return this;
    }

    /**
     * generate AND statement, simultaneous setting value
     *
     * @param column
     * @param value  column value
     * @return
     */
    public PlainFS and(String column, Object value) {
        return this.where(column, value);
    }

    /**
     * @param column
     * @return
     */
    public PlainFS and(String column) {
        return this.where(column);
    }

    /**
     * generate OR statement, simultaneous setting value
     *
     * @param column
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS or(String column, Object value) {

        String trueStatement = mapColumn(column) + " = ?";
        conditionSQL.append(" OR (").append(trueStatement);
        conditionSQL.append(')');
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * generate "!=" statement, simultaneous setting value
     *
     * @param column column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS andNotEq(String column, Object value) {
        conditionSQL.append(AND_STR).append(mapColumn(column)).append(" != ?");
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * generate "!=" statement, simultaneous setting value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS notEq(Object value) {
        conditionSQL.append(" != ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "!= ''" statement
     *
     * @param columnName column name
     * @return SqlGenerator
     */
    public PlainFS andNotEmpty(String columnName) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" != ''");
        return this;
    }

    /**
     * @param columnName
     * @return
     */
    public PlainFS notEmpty(String columnName) {
        conditionSQL.append(mapColumn(columnName)).append(" != ''");
        return this;
    }

    /**
     * generate "!= ''" statement
     *
     * @return
     */
    public PlainFS notEmpty() {
        conditionSQL.append(" != ''");
        return this;
    }

    /**
     * generate "IS NOT NULL" statement
     *
     * @param columnName column name
     * @return
     */
    public PlainFS andNotNull(String columnName) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" IS NOT NULL");
        return this;
    }

    /**
     * generate "IS NOT NULL" statement
     *
     * @param columnName column name
     * @return
     */
    public PlainFS notNull(String columnName) {
        conditionSQL.append(mapColumn(columnName)).append(" IS NOT NULL");
        return this;
    }

    public PlainFS isNull() {
        conditionSQL.append(" IS NULL");
        return this;
    }

    public PlainFS isNull(String columnName) {
        conditionSQL.append(mapColumn(columnName)).append(" IS NULL");
        return this;
    }

    public PlainFS andIsNull(String columnName) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" IS NULL");
        return this;
    }

    /**
     * 默认使用Mysql limit语法
     *
     * @param size
     * @return
     */
    public PlainFS limit(int size) {
        return limit(0, size);
    }

    /**
     * 默认使用Mysql limit语法
     *
     * @param offset
     * @param size
     * @return
     */
    public PlainFS limit(int offset, int size) {
        if (limitSQL.length() == 0) {
            limitSQL.append(" LIMIT ").append(offset).append(", ").append(size);
        }
        return this;
    }

    /**
     * generate like statement, simultaneous setting value
     *
     * @param columnName column name
     * @param value      column value
     * @return SqlGenerator
     */
    public PlainFS andLike(String columnName, Object value) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" LIKE ?");
        paramValues.add("%" + value + "%");
        return this;
    }

    /**
     * generate like statement, simultaneous setting value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS like(Object value) {
        conditionSQL.append(" LIKE ?");
        paramValues.add("%" + value + "%");
        return this;
    }

    /**
     * generate like statement, simultaneous setting value
     *
     * @param columnName column name
     * @param value      column value
     * @return SqlGenerator
     */
    public PlainFS like(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(trueColumnName).append(" LIKE ?");
        paramValues.add("%" + value + "%");
        return this;
    }

    /**
     * @param columnName
     * @param value
     * @return
     */
    public PlainFS andLikeLeft(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" LIKE ?");
        paramValues.add("%" + value);
        return this;
    }

    /**
     * @param value
     * @return
     */
    public PlainFS likeLeft(Object value) {
        conditionSQL.append(" LIKE ?");
        paramValues.add("%" + value);
        return this;
    }

    /**
     * @param columnName
     * @param value
     * @return
     */
    public PlainFS likeLeft(String columnName, Object value) {
        conditionSQL.append(mapColumn(columnName)).append(" LIKE ?");
        paramValues.add("%" + value);
        return this;
    }

    /**
     * @param columnName
     * @param value
     * @return
     */
    public PlainFS andLikeRight(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" LIKE ?");
        paramValues.add(value + "%");
        return this;
    }

    /**
     * @param value
     * @return
     */
    public PlainFS likeRight(Object value) {
        conditionSQL.append(" LIKE ?");
        paramValues.add(value + "%");
        return this;
    }

    /**
     * @param columnName
     * @param value
     * @return
     */
    public PlainFS likeRight(String columnName, Object value) {
        conditionSQL.append(mapColumn(columnName)).append(" LIKE ?");
        paramValues.add(value + "%");
        return this;
    }

    /**
     * generate between statement, simultaneous setting value
     *
     * @param columnName column name
     * @param a          first range value
     * @param b          second range value
     * @return SqlGenerator
     */
    public PlainFS andBetween(String columnName, Object a, Object b) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * not between
     *
     * @param columnName
     * @param a
     * @param b
     * @return
     */
    public PlainFS andNotBetween(String columnName, Object a, Object b) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" NOT BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * generate between values
     *
     * @param a first range value
     * @param b second range value
     * @return SqlGenerator
     */
    public PlainFS between(Object a, Object b) {
        conditionSQL.append(" BETWEEN ? and ?");
        paramValues.add(a);
        paramValues.add(b);
        return this;
    }

    /**
     * not between
     *
     * @param a
     * @param b
     * @return
     */
    public PlainFS notBetween(Object a, Object b) {
        conditionSQL.append(" NOT BETWEEN ? and ?");
        paramValues.add(a);
        paramValues.add(b);
        return this;
    }

    /**
     * generate between statement, simultaneous setting value
     *
     * @param columnName column name
     * @param a          first range value
     * @param b          second range value
     * @return SqlGenerator
     */
    public PlainFS between(String columnName, Object a, Object b) {
        conditionSQL.append(mapColumn(columnName)).append(" BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * not between
     *
     * @param columnName
     * @param a
     * @param b
     * @return
     */
    public PlainFS notBetween(String columnName, Object a, Object b) {
        conditionSQL.append(mapColumn(columnName)).append(" NOT BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * generate ">" statement, simultaneous setting value
     *
     * @param columnName table column name [sql]
     * @param value      column value
     * @return SqlGenerator
     */
    public PlainFS andGt(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" > ?");
        paramValues.add(convertValue(columnName, value));
        return this;
    }

    /**
     * generate ">" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS gt(Object value) {
        conditionSQL.append(" > ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate ">" statement, simultaneous setting value
     *
     * @param columnName table column name [sql]
     * @param value      column value
     * @return SqlGenerator
     */
    public PlainFS gt(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(trueColumnName).append(" > ?");
        paramValues.add(convertValue(columnName, value));
        return this;
    }

    /**
     * generate ">=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS andGte(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" >= ?");
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * generate ">=" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS gte(Object value) {
        conditionSQL.append(" >= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS andLt(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" < ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS lt(Object value) {
        conditionSQL.append(" < ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS andLte(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" <= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<=" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public PlainFS lte(Object value) {
        conditionSQL.append(" <= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate ">=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS gte(String column, Object value) {
        conditionSQL.append(mapColumn(column)).append(" >= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS lt(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" < ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS lte(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" <= ?");
        paramValues.add(value);
        return this;
    }


    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param args   column value
     * @return SqlGenerator
     */
    public PlainFS andIn(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * not in
     *
     * @param column
     * @param args
     * @return
     */
    public PlainFS andNotIn(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" NOT IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param args   column value
     * @return SqlGenerator
     */
    public PlainFS in(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * not in
     *
     * @param column
     * @param args
     * @return
     */
    public PlainFS notIn(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" NOT IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }


    /**
     * generate "in" statement value
     *
     * @param args column value
     * @return SqlGenerator
     */
    public PlainFS in(Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("Column: {}, query params is empty.");
            return this;
        }
        conditionSQL.append(" IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * not in
     *
     * @param args
     * @return
     */
    public PlainFS notIn(Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("Column: {}, query params is empty.");
            return this;
        }
        conditionSQL.append(" NOT IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * Set in params
     *
     * @param list in param values
     * @param <S>
     * @return SqlGenerator
     */
    public <S> PlainFS in(List<S> list) {
        return this.in(list.toArray());
    }

    /**
     * not in
     *
     * @param list
     * @return
     */
    public <S> PlainFS notIn(List<S> list) {
        return this.notIn(list.toArray());
    }

    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column column name
     * @param args   in param values
     * @param <S>
     * @return SqlGenerator
     */
    public <S> PlainFS in(String column, List<S> args) {
        return this.in(column, args.toArray());
    }

    /**
     * not in
     *
     * @param column
     * @param args
     * @return
     */
    public <S> PlainFS notIn(String column, List<S> args) {
        return this.notIn(column, args.toArray());
    }


    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column column name
     * @param args   in param values
     * @param <S>
     * @return SqlGenerator
     */
    public <S> PlainFS andIn(String column, List<S> args) {
        return this.andIn(column, args.toArray());
    }

    /**
     * and not in
     *
     * @param column
     * @param args
     * @return
     */
    public <S> PlainFS andNotIn(String column, List<S> args) {
        return this.andNotIn(column, args.toArray());
    }

    /**
     * generate order by statement
     *
     * @param order like "id desc"
     * @return SqlGenerator
     */
    public PlainFS order(String order) {
        if (StrKit.isBlank(order)) {
            throw new HORMException("order is empty");
        }

        if (this.orderBySQL.length() > 0) {
            this.orderBySQL.append(',');
        }

        String orderTemp = order.trim();
        String newOrder = orderTemp;
        String column = null;
        String flag = null;

        int index = orderTemp.indexOf(" ");
        if (index > -1) {
            column = orderTemp.substring(0, index);
            flag = orderTemp.substring(index);
            newOrder = mapColumn(column) + " " + flag;
        }

        this.orderBySQL.append(' ').append(newOrder);
        return this;
    }

    /**
     * generate order by statement
     *
     * @param columnName column name
     * @param orderBy    order by @see OrderBy
     * @return SqlGenerator
     */
    public PlainFS order(String columnName, OrderBy orderBy) {
        if (this.orderBySQL.length() > 0) {
            this.orderBySQL.append(',');
        }
        String trueColumnName = mapColumn(columnName);
        this.orderBySQL.append(' ').append(trueColumnName).append(' ').append(orderBy.toString());
        return this;
    }

    /**
     * group by
     *
     * @param exps
     * @return
     */
    public PlainFS groupByExp(String exps) {

        if (this.groupBySQL.length() > 0) {
            throw new HORMException("group by sql can only be set once.");
        }

        if (exps == null) {
            throw new HORMException("groupBy 参数不能为空");
        }

        groupBySQL.append(' ').append(exps);
        return this;
    }

    /**
     * 添加group by
     *
     * @param columnNames
     * @return
     */
    public PlainFS groupBy(String... columnNames) {

        if (this.groupBySQL.length() > 0) {
            throw new HORMException("group by sql can only be set once.");
        }

        if (columnNames == null || columnNames.length == 0) {
            throw new HORMException("groupBy 参数不能为空");
        }

        for (String columnName : columnNames) {
            String trueColumnName = mapColumn(columnName);
            groupBySQL.append(' ').append(trueColumnName).append(",");
        }
        groupBySQL.deleteCharAt(groupBySQL.length() - 1);
        return this;
    }

    /**
     * Update columns set value
     *
     * @param column column name
     * @param value  column value
     * @return SqlGenerator
     */
    public PlainFS set(String column, Object value) {
        String trueColumnName = mapColumn(column);
        updateColumns.put(trueColumnName, convertValue(column, value));
        return this;
    }

    /**
     * 批量更新字段
     *
     * @param updateColumns
     * @return
     */
    public PlainFS set(Map<String, Object> updateColumns) {
        if (updateColumns != null && !updateColumns.isEmpty()) {
            updateColumns.forEach(this::set);
        }
        return this;
    }

    /**
     * 更新字段自增
     *
     * @param column
     * @param count
     * @return
     */
    public PlainFS inc(String column, Number count) {
        String trueColumnName = mapColumn(column);
        String exp = trueColumnName + " + " + count;
        setExp(trueColumnName, exp);
        return this;
    }

    /**
     * 更新字段自减
     *
     * @param column
     * @param count
     * @return
     */
    public PlainFS dec(String column, Number count) {
        String trueColumnName = mapColumn(column);
        String exp = trueColumnName + " - " + count;
        setExp(trueColumnName, exp);
        return this;
    }

    /**
     * 更新字段自增 1
     *
     * @param column
     * @return
     */
    public PlainFS inc(String column) {
        return inc(column, 1);
    }

    /**
     * 更新字段自减 1
     *
     * @param column
     * @return
     */
    public PlainFS dec(String column) {
        return dec(column, 1);
    }

    /**
     * 更新字段为native sql
     *
     * @param column
     * @param exp    native sql表达式
     * @return
     */
    public PlainFS setExp(String column, String exp) {
        String trueColumnName = mapColumn(column);
        updateColumnExpression.put(trueColumnName, exp);
        return this;
    }


    public PlainFS orNest(Function<PlainFS, PlainFS> func) {
        return or().nest(func);
    }

    public PlainFS andNest(Function<PlainFS, PlainFS> func) {
        return and().nest(func);
    }

    public PlainFS nest(Function<PlainFS, PlainFS> func) {
        conditionSQL.append("(");
        func.apply(this);
        conditionSQL.append(")");
        return this;
    }

    public PlainFS and() {
        conditionSQL.append(AND_STR);
        return this;
    }

    public PlainFS or() {
        conditionSQL.append(" OR ");
        return this;
    }

    public PlainFS eq(String column, Object value) {
        conditionSQL.append(mapColumn(column)).append(" = ?");
        paramValues.add(value);
        return this;
    }

    public PlainFS andEq(String column, Object value) {
        conditionSQL.append(AND_STR).append(mapColumn(column)).append(" = ?");
        paramValues.add(value);
        return this;
    }

    /////////// NEW API /////////
    public PlainFS ltMeta(String column) {
        conditionSQL.append(" < ").append(mapColumn(column));
        return this;
    }

    /**
     * 条件添加表达式
     *
     * @param exp native sql expression
     * @return
     */
    public PlainFS exp(String exp) {
        conditionSQL.append(exp);
        return this;
    }

    /**
     * 条件添加表达式
     *
     * @param exp native sql expression
     * @return
     */
    public PlainFS andExp(String exp) {
        conditionSQL.append(AND_STR).append(exp);
        return this;
    }

    /**
     * @param exp native sql expression
     * @return
     */
    public PlainFS ltExp(String exp) {
        conditionSQL.append(" < ").append(exp);
        return this;
    }

    public PlainFS lteMeta(String column) {
        conditionSQL.append(" <= ").append(mapColumn(column));
        return this;
    }

    public PlainFS lteExp(String exp) {
        conditionSQL.append(" <= ").append(exp);
        return this;
    }

    public PlainFS ltMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" < ").append(mapColumn(column2));
        return this;
    }

    public PlainFS andLtMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" < ").append(mapColumn(column2));
        return this;
    }

    public PlainFS lteMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" <= ").append(mapColumn(column2));
        return this;
    }

    public PlainFS andLteMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" <= ").append(mapColumn(column2));
        return this;
    }

    /////// NEW API2 //////////
    public PlainFS eqMeta(String column) {
        conditionSQL.append(" = ").append(mapColumn(column));
        return this;
    }

    public PlainFS eqExp(String exp) {
        conditionSQL.append(" = ").append(exp);
        return this;
    }


    public PlainFS eqMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" = ").append(mapColumn(column2));
        return this;
    }

    public PlainFS andEqMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" = ").append(mapColumn(column2));
        return this;
    }

    ///////// NEW API3 //////////
    public PlainFS gtMeta(String column) {
        conditionSQL.append(" > ").append(mapColumn(column));
        return this;
    }

    public PlainFS gtExp(String exp) {
        conditionSQL.append(" > ").append(exp);
        return this;
    }


    public PlainFS gtMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" > ").append(mapColumn(column2));
        return this;
    }

    public PlainFS andGtMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" > ").append(mapColumn(column2));
        return this;
    }

    public PlainFS gteMeta(String column) {
        conditionSQL.append(" >= ").append(mapColumn(column));
        return this;
    }

    public PlainFS gteExp(String exp) {
        conditionSQL.append(" >= ").append(exp);
        return this;
    }

    public PlainFS gteMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" >= ").append(mapColumn(column2));
        return this;
    }

    public PlainFS andGteMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" >= ").append(mapColumn(column2));
        return this;
    }

    public PlainFS notEqMeta(String column) {
        conditionSQL.append(" != ").append(mapColumn(column));
        return this;
    }

    public PlainFS notEqExp(String exp) {
        conditionSQL.append(" != ").append(exp);
        return this;
    }

    public PlainFS notEqMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" != ").append(mapColumn(column2));
        return this;
    }

    public PlainFS andNotEqMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" != ").append(mapColumn(column2));
        return this;
    }

    public PlainFS setNull(String column) {
        return set(column, null);
    }

    /**
     * Update a model
     *
     * @param args
     * @return affect the number of rows
     */

    private void setArguments(Object[] args) {
        for (int i = 0; i < args.length; i++) {
            if (i == args.length - 1) {
                conditionSQL.append("?");
            } else {
                conditionSQL.append("?, ");
            }
            paramValues.add(args[i]);
        }
    }

    /**
     * pre check
     */
    private void beforeCheck() {
        if (StrKit.isBlank(tableName)) {
            throw new HORMException("table name is null");
        }
    }

    private Object convertValue(TypeHandler<?> typeHandler, Object v) {
        if (typeHandler == null) {
            return v;
        }

        TypedParameter tp = new TypedParameter();
        tp.setParameter(v);
        tp.setTypeHandler(typeHandler);

        return tp;
    }

    private Object convertValue(String column, Object value) {
        return convertValue(typeHandlerMap.get(column), value);
    }

    public StringBuilder getConditionSQL() {
        return conditionSQL;
    }

    public StringBuilder getOrderBySQL() {
        return orderBySQL;
    }

    public StringBuilder getGroupBySQL() {
        return groupBySQL;
    }

    public StringBuilder getLimitSQL() {
        return limitSQL;
    }

    public List<Object> getParamValues() {
        return paramValues;
    }

    public Map<String, Object> getUpdateColumns() {
        return updateColumns;
    }

    public Map<String, String> getUpdateColumnExpression() {
        return updateColumnExpression;
    }

    public String getSelectColumns() {
        return selectColumns;
    }

    public String getSelectExpressionColumns() {
        return selectExpressionColumns;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public boolean isCount() {
        return count;
    }

    public String[] getPrimaryKeyColumn() {
        return primaryKeyColumn;
    }

    public String getTableName() {
        return tableName;
    }

    public Map<String, TypeHandler<?>> getTypeHandlerMap() {
        return typeHandlerMap;
    }

    public String getPreEscape() {
        return preEscape;
    }

    public String getPostEscape() {
        return postEscape;
    }

    public String getHint() {
        return hint;
    }

    public String getTablePostfix() {
        return tablePostfix;
    }

    public String getTablePrefix() {
        return tablePrefix;
    }
}
